package com.modules.service.generator.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.common.entity.ApplicationEntity;
import com.common.vo.Result;
import com.modules.dao.generator.CodeGeneratorDao;
import com.modules.entity.generator.CodeGeneratorEntity;
import com.modules.entity.generator.GeneratorFieldEntity;
import com.modules.entity.generator.GeneratorTableEntity;
import com.modules.service.generator.CodeGeneratorService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * service日志
 *
 * @author yyq
 */
@Service
public class CodeGeneratorServiceImp extends ServiceImpl<CodeGeneratorDao, CodeGeneratorEntity> implements CodeGeneratorService {

    @Resource
    private ApplicationEntity applicationEntity;

    /** 查询所有表*/
    @Override
    public Result<?> selectTablePage(Page<GeneratorTableEntity> page) {
        String url = applicationEntity.getDatasource().getUrl();
        String schema = url.substring(0, url.indexOf("?"));// 获取数据库名称
        schema = schema.substring(schema.lastIndexOf("/") + 1);
        IPage<GeneratorTableEntity> iPage = baseMapper.selectTablePage(page, schema);
        return Result.page(iPage.getRecords(), iPage.getTotal());
    }

    /** 查询某个表字段*/
    @Override
    public Result<?> selectTableFieldPage(String name) {
        String url = applicationEntity.getDatasource().getUrl();
        String schema = url.substring(0, url.indexOf("?"));// 获取数据库名称
        schema = schema.substring(schema.lastIndexOf("/") + 1);
        List<GeneratorFieldEntity> list = baseMapper.selectTableFieldPage(name, schema);
        return Result.page(list, Long.valueOf(list.size()));
    }


    /** 包名*/
    String moduleName = "";
    /** 表名前缀*/
    String beginName = "";
    // 当前项目路径
    String projectPath = "";

    /**
     *  生成代码
     **/
    @Override
    public Result<?> createOneCode(String tableNames, String fieldNames, String tableComments) {
        // 查询配置
        LambdaQueryWrapper<CodeGeneratorEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(CodeGeneratorEntity::getState, 1);
        CodeGeneratorEntity selectOne = null;
        List<CodeGeneratorEntity> selectList = baseMapper.selectList(queryWrapper);
        if(CollUtil.isNotEmpty(selectList)){
            selectOne = selectList.get(0);
        }

        /** 数据源配置*/
        String url = applicationEntity.getDatasource().getUrl();
        String driverName = applicationEntity.getDatasource().getDriverClassName();
        String username = applicationEntity.getDatasource().getUsername();
        String password = applicationEntity.getDatasource().getPassword();



        /** 包名*/
        moduleName = "";
        /** 表名前缀*/
        beginName = "";

        String str = tableNames.split(",")[0];

        /** 是否截取前缀*/
        if(selectOne.getBeginTn() != null && selectOne.getBeginTn().equals(1)){
            if(StrUtil.isNotBlank(selectOne.getBeginTableName())){
                beginName = selectOne.getBeginTableName();
                moduleName = selectOne.getBeginTableName().replace("_", "");
                str = str.replace(selectOne.getBeginTableName(), "").toUpperCase();
            }
        }

        /** 名称标题*/
        String title = tableComments;
        // 获取未转驼峰式前的表(转小写)
        String newTable = str.toLowerCase();
        /** 生成声明java对象前缀*/
        String entityMin = strByKey(str,"_");
        /** 需要生成代码的表*/
        String [] tables = tableNames.split(",");
        
        // 官方网站：https://baomidou.com/pages/d357af/#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5

        // 参考网站：https://blog.csdn.net/kinghmj01/article/details/97748509

        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        // 当前项目路径
        projectPath = System.getProperty("user.dir");

        if(StrUtil.isNotBlank(selectOne.getProjectPath())){
            projectPath = selectOne.getProjectPath();
        }

        // 当前项目的下的路径
        gc.setOutputDir(projectPath + (StrUtil.isNotBlank(selectOne.getComPath())?selectOne.getComPath():""));
        // 作者
        gc.setAuthor(selectOne.getAuthor());
        // 是否打开输出目录 默认为true
        gc.setOpen(selectOne.getOpenState().equals(1));
        // 实体属性 Swagger2 注解
        gc.setSwagger2(selectOne.getSwagger2().equals(1));

        // 自定义文件命名，注意 %s 会自动填充表实体属性！
        gc.setControllerName(StrUtil.isNotBlank(selectOne.getController())?"%s"+selectOne.getController():"%sController");
        gc.setServiceName(StrUtil.isNotBlank(selectOne.getService())?"%s"+selectOne.getService():"%sService");
        gc.setServiceImplName(StrUtil.isNotBlank(selectOne.getServiceImpl())?"%s"+selectOne.getServiceImpl():"%sServiceImpl");
        gc.setMapperName(StrUtil.isNotBlank(selectOne.getDao())?"%s"+selectOne.getDao():"%sDao");
        gc.setEntityName(StrUtil.isNotBlank(selectOne.getEntity())?"%s"+selectOne.getEntity():"%sEntity");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        // dsc.setSchemaName("public");
        dsc.setUrl(url);
        dsc.setDriverName(driverName);
        dsc.setUsername(username);
        dsc.setPassword(password);
        mpg.setDataSource(dsc);

        // 设置包名
        PackageConfig pc = new PackageConfig();
        // 用于包名、表名前缀
        //pc.setModuleName(moduleName);
        // 生成到那些包下 如 com.modules主包下的 controller.sys.TestControlle
        pc.setParent(StrUtil.isNotBlank(selectOne.getModulesPath())?selectOne.getModulesPath():"com.modules");
        pc.setController(StrUtil.isNotBlank(selectOne.getControllerPath())?selectOne.getControllerPath().replace("${package}", moduleName):"controller");
        pc.setService(StrUtil.isNotBlank(selectOne.getServicePath())?selectOne.getServicePath().replace("${package}", moduleName):"service");
        pc.setServiceImpl(StrUtil.isNotBlank(selectOne.getServiceImplPath())?selectOne.getServiceImplPath().replace("${package}", moduleName):"service.impl");
        pc.setMapper(StrUtil.isNotBlank(selectOne.getDaoPath())?selectOne.getDaoPath().replace("${package}", moduleName):"dao");
        pc.setEntity(StrUtil.isNotBlank(selectOne.getEntityPath())?selectOne.getEntityPath().replace("${package}", moduleName):"entity");
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() { // 自定义配置 ${dtoReq}
                Map<String, Object> map = MapUtil.newHashMap();
                map.put("dtoReq", pc.getParent()+".dto."+moduleName+".req");
                map.put("dtoResp", pc.getParent()+".dto."+moduleName+".resp");
                map.put("controllerUrl", moduleName);
                map.put("title", title);
                map.put("entityMin", entityMin);
                this.setMap(map);
            }
        };


        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        //String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置（自定义）
        List<FileOutConfig> focList = CollUtil.newArrayList();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return projectPath + "/src/main/resources/mapper/" + moduleName + "/" + entityMin + "Mapper" + StringPool.DOT_XML;
            }
        });
        focList.add(new FileOutConfig("/templates/ftl/req.java.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/com/modules/dto/" + moduleName + "/req/" + tableInfo.getEntityName() + "Req" + StringPool.DOT_JAVA;
            }
        });
        focList.add(new FileOutConfig("/templates/ftl/resp.java.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/com/modules/dto/" + moduleName + "/resp/" + tableInfo.getEntityName() + "Resp" + StringPool.DOT_JAVA;
            }
        });
        focList.add(new FileOutConfig("/templates/ftl/controller.java.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/com/modules/controller/" + moduleName + "/" + tableInfo.getControllerName() + StringPool.DOT_JAVA;
            }
        });
        // 生成前端文件
        focList.add(new FileOutConfig("/templates/ftl/page.js.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/resources/static/js/" + moduleName + "/" + newTable + ".js";
            }
        });
        focList.add(new FileOutConfig("/templates/ftl/page.html.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/resources/templates/" + moduleName + "/" + newTable + ".html";
            }
        });

        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判断自定义文件夹是否需要创建
                checkDir("调用默认方法创建的目录，自定义目录用");
                if (fileType == FileType.MAPPER) {
                    // 已经生成 mapper 文件判断存在，不想重新生成返回 false
                    return !new File(filePath).exists();
                }
                // 允许生成模板文件
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        //指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();

        //需要生成的表
        strategy.setInclude(tables);
        strategy.setControllerMappingHyphenStyle(true);
        //类名生成策略：驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //字段名生成方式：驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 表前缀
        strategy.setTablePrefix(beginName);


        // 每层的继承（不需要可不设置）
        strategy.setSuperControllerClass("com.common.controller.BaseController");
        strategy.setSuperServiceClass("com.baomidou.mybatisplus.extension.service.IService");
        strategy.setSuperServiceImplClass("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
        strategy.setSuperMapperClass("com.baomidou.mybatisplus.core.mapper.BaseMapper");
        //继承的属性父类
        strategy.setSuperEntityClass("com.common.entity.BaseEntity");

        // 写于父类中的公共字段（在父类中已经有的不需要生成的字段）
        String field[] = null;
        if(StrUtil.isNotBlank(fieldNames)){
            field = fieldNames.split(",");
        }else{
            field = StrUtil.isNotBlank(selectOne.getNotField())?selectOne.getNotField().split(","):null;
        }
        strategy.setSuperEntityColumns(field);

        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);

        mpg.setStrategy(strategy);

        mpg.setTemplateEngine(new FreemarkerTemplateEngine());

        mpg.execute();
        return Result.success("生成代码成功");
    }

    /**
     *  生成代码-多表
     **/
    @Override
    public Result<?> createCodes(String tableNames, String tableComments) {
        String arr [] = tableNames.split(StrUtil.COMMA);
        String title [] = tableComments.split(StrUtil.COMMA);
        for (int i = 0; i < arr.length; i++) {
            createOneCode(arr[i], null, title[i]);
        }
        return Result.success("生成代码成功");
    }

    /**
     *  生成代码-选择多表（暂时不用）
     **/
    @Override
    public Result<?> createCodeTables(String tableNames, String tableComments) {
        // 查询配置
        LambdaQueryWrapper<CodeGeneratorEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(CodeGeneratorEntity::getState, 1);
        CodeGeneratorEntity selectOne = null;
        List<CodeGeneratorEntity> selectList = baseMapper.selectList(queryWrapper);
        if(CollUtil.isNotEmpty(selectList)){
            selectOne = selectList.get(0);
        }
        /** 数据源配置*/
        String url = applicationEntity.getDatasource().getUrl();
        String driverName = applicationEntity.getDatasource().getDriverClassName();
        String username = applicationEntity.getDatasource().getUsername();
        String password = applicationEntity.getDatasource().getPassword();



        /** 包名*/
        moduleName = "";
        /** 表名前缀*/
        beginName = "";

        String str = tableNames.split(",")[0];

        /** 是否截取前缀*/
        if(selectOne.getBeginTn() != null && selectOne.getBeginTn().equals(1)){
            if(StrUtil.isNotBlank(selectOne.getBeginTableName())){
                beginName = selectOne.getBeginTableName();
                moduleName = selectOne.getBeginTableName().replace("_", "");
                str = str.replace(selectOne.getBeginTableName(), "").toUpperCase();
            }
        }

        /** 名称标题*/
        String title = tableComments;

        /** 生成声明java对象前缀*/
        String entityMin = strByKey(str,"_");
        /** 需要生成代码的表*/
        String [] tables = tableNames.split(",");
        //String [] titles = tableComments.split(",");

        // 官方网站：https://baomidou.com/pages/d357af/#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5

        // 参考网站：https://blog.csdn.net/kinghmj01/article/details/97748509

        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        // 当前项目路径
        projectPath = System.getProperty("user.dir");

        if(StrUtil.isNotBlank(selectOne.getProjectPath())){
            projectPath = selectOne.getProjectPath();
        }

        // 当前项目的下的路径
        gc.setOutputDir(projectPath + (StrUtil.isNotBlank(selectOne.getComPath())?selectOne.getComPath():""));
        // 作者
        gc.setAuthor(selectOne.getAuthor());
        // 是否打开输出目录 默认为true
        gc.setOpen(selectOne.getOpenState().equals(1));
        // 实体属性 Swagger2 注解
        gc.setSwagger2(selectOne.getSwagger2().equals(1));

        // 自定义文件命名，注意 %s 会自动填充表实体属性！
        gc.setControllerName(StrUtil.isNotBlank(selectOne.getController())?"%s"+selectOne.getController():"%sController");
        gc.setServiceName(StrUtil.isNotBlank(selectOne.getService())?"%s"+selectOne.getService():"%sService");
        gc.setServiceImplName(StrUtil.isNotBlank(selectOne.getServiceImpl())?"%s"+selectOne.getServiceImpl():"%sServiceImpl");
        gc.setMapperName(StrUtil.isNotBlank(selectOne.getDao())?"%s"+selectOne.getDao():"%sDao");
        gc.setEntityName(StrUtil.isNotBlank(selectOne.getEntity())?"%s"+selectOne.getEntity():"%sEntity");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        // dsc.setSchemaName("public");
        dsc.setUrl(url);
        dsc.setDriverName(driverName);
        dsc.setUsername(username);
        dsc.setPassword(password);
        mpg.setDataSource(dsc);

        // 设置包名
        PackageConfig pc = new PackageConfig();
        // 用于包名、表名前缀
        //pc.setModuleName(moduleName);
        // 生成到那些包下 如 com.modules主包下的 controller.sys.TestControlle
        /*pc.setParent("com.modules");
        pc.setController("controller");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setMapper("dao");
        pc.setEntity("entity");*/
        pc.setParent(StrUtil.isNotBlank(selectOne.getModulesPath())?selectOne.getModulesPath():"com.modules");
        pc.setController(StrUtil.isNotBlank(selectOne.getControllerPath())?selectOne.getControllerPath().replace("${package}", moduleName):"controller");
        pc.setService(StrUtil.isNotBlank(selectOne.getServicePath())?selectOne.getServicePath().replace("${package}", moduleName):"service");
        pc.setServiceImpl(StrUtil.isNotBlank(selectOne.getServiceImplPath())?selectOne.getServiceImplPath().replace("${package}", moduleName):"service.impl");
        pc.setMapper(StrUtil.isNotBlank(selectOne.getDaoPath())?selectOne.getDaoPath().replace("${package}", moduleName):"dao");
        pc.setEntity(StrUtil.isNotBlank(selectOne.getEntityPath())?selectOne.getEntityPath().replace("${package}", moduleName):"entity");
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() { // 自定义配置 ${dtoReq}
                Map<String, Object> map = MapUtil.newHashMap();
                map.put("controllerUrl", moduleName);
                map.put("title", "");
                map.put("entityMin", "");
                this.setMap(map);
            }
        };


        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        //String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置（自定义）
        List<FileOutConfig> focList = CollUtil.newArrayList();

        for (String s : tables) {
            /** 是否截取前缀*/
            String name = "";
            String tempName = s;
            if(selectOne.getBeginTn() != null && selectOne.getBeginTn().equals(1)){
                if(StrUtil.isNotBlank(selectOne.getBeginTableName())){
                    moduleName = selectOne.getBeginTableName().replace("_", "");
                    tempName = s.replace(selectOne.getBeginTableName(), "");
                    name = tempName.toUpperCase();
                }
                /** 生成声明java对象前缀*/
                name = strByKey(name,"_");
            }
            setListTemplate(templatePath, focList, moduleName, name, tempName);
        }
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();

        //需要生成的表
        strategy.setInclude(tables);
        strategy.setControllerMappingHyphenStyle(true);
        //类名生成策略：驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //字段名生成方式：驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 表前缀
        strategy.setTablePrefix(beginName);


        // 每层的继承（不需要可不设置）
        strategy.setSuperControllerClass("com.common.controller.BaseController");
        strategy.setSuperServiceClass("com.baomidou.mybatisplus.extension.service.IService");
        strategy.setSuperServiceImplClass("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
        strategy.setSuperMapperClass("com.baomidou.mybatisplus.core.mapper.BaseMapper");
        //继承的属性父类
        strategy.setSuperEntityClass("com.common.entity.BaseEntity");

        // 写于父类中的公共字段（在父类中已经有的不需要生成的字段）
        String field[] = StrUtil.isNotBlank(selectOne.getNotField())?selectOne.getNotField().split(","):null;

        strategy.setSuperEntityColumns(field);

        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);

        mpg.setStrategy(strategy);

        mpg.setTemplateEngine(new FreemarkerTemplateEngine());

        mpg.execute();
        return Result.success("生成代码成功");
    }

    /***
     * 设置需要生成的模板
     * @param templatePath
     * @param focList
     * @param moduleName
     * @param name
     */
    public void setListTemplate(String templatePath, List<FileOutConfig> focList, String moduleName, String name, String tablename){
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return projectPath + "/src/main/resources/mapper/" + moduleName + "/" + name + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*focList.add(new FileOutConfig("/templates/ftl/controller.java.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/com/modules/controller/" + moduleName + "/" + tableInfo.getControllerName() + StringPool.DOT_JAVA;
            }
        });*/
        // 生成前端文件
        focList.add(new FileOutConfig("/templates/ftl/page.js.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/resources/static/js/" + moduleName + "/" + tablename + ".js";
            }
        });
        focList.add(new FileOutConfig("/templates/ftl/page.html.ftl") {// templatePath
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/resources/templates/" + moduleName + "/" + tablename + ".html";
            }
        });
    }


    /**
     * 转驼峰
     * @param mapKey
     * @param chart
     * @return
     */
    public static String strByKey(String mapKey, String chart){
        String[] split = mapKey.split(chart);
        StringBuilder sbui = new StringBuilder();
        for (int i = 0; i < split.length; i++) {
            if (i==0){
                sbui.append(split[0].toLowerCase());
            }else {
                sbui.append(split[i].charAt(0)+split[i].substring(1).toLowerCase());
            }
        }
        return sbui.toString();
    }



}
